#include "camera.h"

Camera::Camera() : _position(10, -10, 10), _up(0, 0, 1.0f),
    _minZoom(10.0f), _maxZoom(30.0f), _zoomFactor(0.01f),
    _minAngle(M_PI_2 * 0.1f), _maxAngle(M_PI_2 * 0.9f), _rotFactor(0.005f)
{
    _zoom = 20.0f;

    _position.normalize();
    _position *= _zoom;
}

Camera::~Camera()
{
    //dtor
}

void Camera::recalculate()
{
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(_position.x, _position.y, _position.z,
              0.0f, 0.0f, 0.0f,
              0.0f, 0.0f, 1.0f);
}

void Camera::updateView(float dx, float dy)
{
    float angle1 = dx * _rotFactor;
    float angle2 = dy * _rotFactor;

    Matrix3f rot1 = Matrix3f::createRotationAroundAxis(0, 0, angle1);
    _position = rot1 * _position;

    Vector3f look = -_position;
    Vector3f rot2axis = look.crossProduct(Vector3f(0, 0, 1.0f));
    rot2axis.normalize();

    Matrix3f rot2;
    axisAngleToMatrix(rot2axis, angle2, rot2);
    Vector3f tmp = rot2 * _position;

    // Limit view angle
    Vector3f tmp2 = _position;
    tmp2.normalize();

    Vector3f level(_position.x, _position.y, 0.0f);
    level.normalize();

    float angle = acos(tmp2.dotProduct(level));

    if (angle < _minAngle && dy > 0) {
        return;
    } else if (angle > _maxAngle && dy < 0) {
        return;
    }


    _position = tmp;

    recalculate();
}

void Camera::updateZoom(float dr)
{
    Vector3f look = _position;
    look.normalize();
    look *= dr;

    Vector3f tmp = _position + look;
    if (tmp.length() < _minZoom || tmp.length() > _maxZoom) {
        return;
    }
    _position = tmp;
    recalculate();
}

const Vector3f& Camera::getPosition() const
{
    return _position;
}

void Camera::axisAngleToMatrix(Vector3f axis, float angle, Matrix3f& m)
{
    float angleCos = cos(angle);
    float angleSin = sin(angle);

    m.at(0, 0) = angleCos + axis.x*axis.x*(1 - angleCos);
    m.at(0, 1) = axis.y*axis.x*(1 - angleCos) + axis.z*angleSin;
    m.at(0, 2) = axis.z*axis.x*(1 - angleCos) - axis.y*angleSin;

    m.at(1, 0) = axis.x*axis.y*(1 - angleCos) - axis.z*angleSin;
    m.at(1, 1) = angleCos + axis.y*axis.y*(1 - angleCos);
    m.at(1, 2) = axis.z*axis.y*(1 - angleCos) + axis.x*angleSin;

    m.at(2, 0) = axis.x*axis.z*(1 - angleCos) + axis.y*angleSin;
    m.at(2, 1) = axis.y*axis.z*(1 - angleCos) - axis.x*angleSin;
    m.at(2, 2) = angleCos + axis.z*axis.z*(1 - angleCos);

}












